﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Xml;
using WPFCanvas.CustomControls;

namespace DiagramDesigner
{
    public enum PenMode
    {
        Select,
        Hand
    }

    public partial class DesignerCanvas : Canvas
    {
        private List<BaseNodeInfo> baseNodes = new List<BaseNodeInfo>();
        public List<BaseNodeInfo> BaseNodes { get { return baseNodes; } }


        public PenMode Mode { set; get;}
        
        private Point? rubberbandSelectionStartPoint = null;

        private SelectionService selectionService;
        internal SelectionService SelectionService
        {
            get
            {
                if (selectionService == null)
                    selectionService = new SelectionService(this);

                return selectionService;
            }
        }

        /// <summary>
        /// 选择界面中的项
        /// </summary>
        public object SelectedObject
        {
            get { return GetValue(SelectedObjectProperty); }
            set { SetValue(SelectedObjectProperty, value); }
        }
        public static readonly DependencyProperty SelectedObjectProperty =
          DependencyProperty.Register("SelectedObject",
                                       typeof(object),
                                       typeof(DesignerCanvas),
                                       new FrameworkPropertyMetadata(false));



        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            if (e.Source == this)
            {
                // in case that this click is the start of a 
                // drag operation we cache the start point
                this.rubberbandSelectionStartPoint = new Point?(e.GetPosition(this));

                if (Mode == PenMode.Select)
                {
                    // if you click directly on the canvas all 
                    // selected items are 'de-selected'
                    SelectionService.ClearSelection();
                   
                }
                Focus();
                e.Handled = true;
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            // if mouse button is not pressed we have no drag operation, ...
            if (e.LeftButton != MouseButtonState.Pressed)
                this.rubberbandSelectionStartPoint = null;

            // ... but if mouse button is pressed and start
            // point value is set we do have one
            if (this.rubberbandSelectionStartPoint.HasValue)
            {
                if (Mode == PenMode.Select)
                {
                    // create rubberband adorner
                    AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(this);
                    if (adornerLayer != null)
                    {
                        RubberbandAdorner adorner = new RubberbandAdorner(this, rubberbandSelectionStartPoint);
                        if (adorner != null)
                        {
                            adornerLayer.Add(adorner);
                        }
                    }
                }
                else
                {
                    var scrollViewer = this.Parent as ScrollViewer;
                    var endPoint = e.GetPosition(this);
                    var changePoint = rubberbandSelectionStartPoint.Value - endPoint;
                    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + changePoint.X * 0.2);
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + changePoint.Y * 0.2);
                }
            }
            e.Handled = true;
        }

        protected override void OnDrop(DragEventArgs e)
        {
            base.OnDrop(e);
            DragObject dragObject = e.Data.GetData(typeof(DragObject)) as DragObject;
            
            if (dragObject != null && !String.IsNullOrEmpty(dragObject.Xaml))
            {
                ContentControl content = (ContentControl)XamlReader.Load(XmlReader.Create(new StringReader(dragObject.Xaml)));
                //给项目添加属性对象
                var di = content as DrawItem;
                if (di == null)
                    return;

                Point position = e.GetPosition(this);

                CanvasAddNode(di, position, Guid.Empty);

                e.Handled = true;
            }
        }

        protected override Size MeasureOverride(Size constraint)
        {
            Size size = new Size();

            foreach (UIElement element in this.InternalChildren)
            {
                double left = Canvas.GetLeft(element);
                double top = Canvas.GetTop(element);
                left = double.IsNaN(left) ? 0 : left;
                top = double.IsNaN(top) ? 0 : top;

                //measure desired size for each child
                element.Measure(constraint);

                Size desiredSize = element.DesiredSize;
                if (!double.IsNaN(desiredSize.Width) && !double.IsNaN(desiredSize.Height))
                {
                    size.Width = Math.Max(size.Width, left + desiredSize.Width);
                    size.Height = Math.Max(size.Height, top + desiredSize.Height);
                }
            }
            // add margin 
            size.Width += 10;
            size.Height += 10;
            return size;
        }

        private void SetConnectorDecoratorTemplate(DesignerItem item)
        {
            if (item.ApplyTemplate() && item.Content is UIElement)
            {
                ControlTemplate template = DesignerItem.GetConnectorDecoratorTemplate(item.Content as UIElement);
                Control decorator = item.Template.FindName("PART_ConnectorDecorator", item) as Control;
                if (decorator != null && template != null)
                    decorator.Template = template;
            }
        }

        public void CanvasAddNode(DrawItem di, Point position, Guid parent)
        {
            BaseNodeInfo bnInfo = NodeInfoFactory.NewNodeInfo(di.ControlType, di.Text , di.Text,di.Tag);
            if (bnInfo == null)
                return;

            bnInfo.DesginerCanvas = this;
            di.DataContext = di.Tag;

            DesignerItem designerItem = new DesignerItem();
            designerItem.Width = di.Width;
            designerItem.Height = di.Height + 30;
            designerItem.Content = di;

            bnInfo.ControlID = designerItem.ID;
            bnInfo.ParentID = parent;

            baseNodes.Add(bnInfo);

            //if (dragObject.DesiredSize.HasValue)
            //{
            //    Size desiredSize = dragObject.DesiredSize.Value;
            //    designerItem.Width = desiredSize.Width;
            //    designerItem.Height = desiredSize.Height;
            //    DesignerCanvas.SetLeft(designerItem, Math.Max(0, position.X - designerItem.Width / 2));
            //    DesignerCanvas.SetTop(designerItem, Math.Max(0, position.Y - designerItem.Height / 2));
            //}
            //else
            //{
            //    DesignerCanvas.SetLeft(designerItem, Math.Max(0, position.X));
            //    DesignerCanvas.SetTop(designerItem, Math.Max(0, position.Y));
            //}

            DesignerCanvas.SetLeft(designerItem, Math.Max(0, position.X));
            DesignerCanvas.SetTop(designerItem, Math.Max(0, position.Y));

            Canvas.SetZIndex(designerItem, this.Children.Count);
            this.Children.Add(designerItem);
            SetConnectorDecoratorTemplate(designerItem);

            //update selection
            designerItem.Loaded += (s, ev) =>
            {
                designerItem.Focus();
                this.SelectionService.SelectItem(designerItem);
            };

            if (parent != Guid.Empty)
            {
                var parentNode = baseNodes.FirstOrDefault(u => u.ControlID == parent);
                if (parentNode == null)
                    return;

                var lineInfo = NodeInfoFactory.NewLineInfo(parentNode, bnInfo, false);
                if (lineInfo == null)
                    return;

                CanvasAddLineNode(parent, ConnectorOrientation.Bottom.ToString(), designerItem.ID, ConnectorOrientation.Top.ToString(), this.Children.Count + 1, lineInfo);
                baseNodes.Add(lineInfo);
            }
        }

        

        private void CanvasAddLineNode(Guid fromItem, string fromGO, Guid toItem, string toGO, int zIndex, BaseLineInfo bli)
        {
            Connector sourceConnector = GetConnector(fromItem, fromGO);
            Connector sinkConnector = GetConnector(toItem, toGO);

            Connection connection = new Connection(sourceConnector, sinkConnector, bli);
            Canvas.SetZIndex(connection, zIndex);
            this.Children.Add(connection);

            bli.ControlID = connection.ID;
            bli.DesginerCanvas = this;

            SelectionService.AddToSelection(connection);
        }


    }
}
